package SymbolTable;

import java.util.*;

import FlowGraph.FlowGraphNode;
import util.InfoCollector;
import db.ClassData;

//////////////////////////////////////////////////////////////////////////
//// SymbolTable
/**

@author Ivan Jeukens
@version v 0.1 03/10/2001
*/
public class SymbolTable {

    public SymbolTable() {
        _elements = new HashMap();
        _nextTable = null;
    }

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    /* node e o primeiro no a ser processado. StopNode vai indicar a
    parada, alem dos nos de quebra de fluxo. currentST e a ST atual
    que tem que ser substituida. parent e a ST pai da currentST */
    public static void updateNodeTable(FlowGraphNode node,
            FlowGraphNode stopNode, SymbolTable currentST,
            SymbolTable parent) {
        LinkedList process = new LinkedList();
        process.add(node.getOutputNode(0));
        HashMap processed = new HashMap();
        processed.put(node, null);
        while(process.size() > 0) {
            FlowGraphNode fgnode = (FlowGraphNode) process.removeFirst();
            processed.put(fgnode, null);
            SymbolTable fgst = fgnode.getSymbolTable();
            if(fgst == currentST) {
                fgnode.setSymbolTable(parent, false);
            }
            else
            if(fgst.getNextTable() == currentST) {
                fgst.link(parent);
            }

            if(fgnode == stopNode ||
               fgnode.getType() == FlowGraphNode.CONTINUE ||
               fgnode.getType() == FlowGraphNode.RETURN ||
               fgnode.getType() == FlowGraphNode.BREAK) continue;

            for(int k = 0;k < fgnode.outputSize();k++) {
                FlowGraphNode on = fgnode.getOutputNode(k);
                if(!processed.containsKey(on)) {
                    process.add(on);
                }
            }
        }
    }

    // Dado um no e sua nova ST, atualiza o link de todas as ST dos nos que 
    // tem caminho a partir desse no
     public static void updateNodesST(FlowGraphNode node,
            FlowGraphNode stopNode, SymbolTable newst,
            SymbolTable parentST) {
        LinkedList process = new LinkedList();

        Iterator ite = FlowGraphNode.nonExceptionBackOutputNodes(node);
        while(ite.hasNext()) {
            process.add(ite.next());
        }
        HashMap processed = new HashMap();
        processed.put(node, null);
        while(process.size() > 0) {
            FlowGraphNode fgnode = (FlowGraphNode) process.removeFirst();
            processed.put(fgnode, null);

            SymbolTable fgst = fgnode.getSymbolTable();
            if(fgst == parentST) {
                fgnode.setSymbolTable(newst, false);
            }
            else
            if(fgst.getNextTable() == parentST) {
                fgst.link(newst);
            }

            if(fgnode == stopNode ||
               fgnode.getType() == FlowGraphNode.CONTINUE ||
               fgnode.getType() == FlowGraphNode.RETURN ||
               fgnode.getType() == FlowGraphNode.BREAK) continue;

            ite = FlowGraphNode.nonExceptionBackOutputNodes(node);
            while(ite.hasNext()) {
                FlowGraphNode on = (FlowGraphNode) ite.next();
                if(!processed.containsKey(on)) {
                    process.add(on);
                }
            }
        }
    }

    public void visitLocalEntries(LinkedList collectors, ClassData cinfo) {
        Object [] col = (Object []) collectors.toArray();
        Iterator ite = _elements.values().iterator();
        while(ite.hasNext()) {
            Object obj = ite.next();
            if(obj instanceof STEntry) {
                for(int j = 0;j < col.length;j++) {
                    ((InfoCollector)col[j]).visitSTEntry((STEntry)obj, 
                        _owner, cinfo);
                }
            }
            // Entao e uma lista de metodos
            else {
                LinkedList methods = (LinkedList) obj;
                for(int i = 0;i < methods.size();i++) {
                    STMethodEntry me = (STMethodEntry) methods.get(i);
                    me.getFlowGraph().visitNodes(collectors);
                }
            }
        }
    }

    public Object clone() {
        SymbolTable ret = new SymbolTable();
        Collection c = _elements.values();
        Iterator ite = c.iterator();
        while(ite.hasNext()) {
            STEntry entry = (STEntry) ite.next();
            ret.putElement(entry.getName(), entry);
        }
        ret.link(_nextTable);
        return ret;
    }

    public void clear() {
        _elements.clear();
    }

    public void link(SymbolTable st) {
        _nextTable = st;
    }

    public SymbolTable getNextTable() {
        return _nextTable;
    }

    public int localSize() {
        return _elements.size();
    }

    public int size() {
        int ret = _elements.size();
        if(_nextTable != null) {
           ret += _nextTable.size();
        }
        return ret;
    }

    public void putElement(String key, Object element) {
        /*if(_elements.containsKey(key)) {
            System.out.println("Symbol Table error: element " + 
                key + " already present.");
            return;
        }*/
        _elements.put(key, element);
    }
    
    public Object getElement(String key) {
        return _elements.get(key);
    }
    
    public Object find(String name) {
        //System.out.println("FIND : " + name);
        Object ret = _elements.get(name);
        if(_nextTable == this) {
            System.out.println("PORRA TO APONTANDO PRA MIM MESMO!!!!");
            return null;
        }
        if(ret == null) {
            if(_nextTable != null) {
                ret = _nextTable.find(name);
            }
        }
        return ret;
    }

    public Object removeElement(String key) {
        return _elements.remove(key);
    }

    public Collection getAllElements() {
        LinkedList ret = new LinkedList(_elements.values());
        if(_nextTable != null) {
            ret.addAll(_nextTable.getAllElements());
        }
        return ret;
    }

    public Collection getElements() {
        Collection c = _elements.values();
        return c;
    }

    /* Esse metodo retorna um iterator para os elementos somente desta tabela! */
    public Iterator iterator() {
        Collection c = getElements();
        return c.iterator();
    }

    public boolean hasElement(String key ) {
        return _elements.containsKey(key);
    }

    public boolean hasElement(STEntry entry) {
        Iterator ite = _elements.values().iterator();
        while(ite.hasNext()) {
            STEntry e2 = (STEntry) ite.next();
            if(entry == e2) return true;
        }
        return false;
    }

    public String toString() {
        StringBuffer ret = new StringBuffer();

        ret.append("Local table:  \n");

        Collection values = _elements.values();
        Iterator ite = values.iterator();
        while(ite.hasNext()) {
            STEntry entry = (STEntry) ite.next();
            ret.append(entry + "\n");
        }

        if(_nextTable != null) {
            ret.append(_nextTable.toString());
        }

        return ret.toString();
    }

    public String localEntriesToString() {
        StringBuffer ret = new StringBuffer();

        Collection values = _elements.values();
        Iterator ite = values.iterator();
        while(ite.hasNext()) {
            STEntry entry = (STEntry) ite.next();
            ret.append(entry + "\n");
        }
        return ret.toString();
    }

    public void setOwner(FlowGraphNode node) {
        _owner = node;
    }

    public FlowGraphNode getOwner() {
        return _owner;
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////


    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private HashMap _elements;

    private SymbolTable _nextTable;
    private FlowGraphNode _owner;
}
